﻿using System;
using System.Reflection;
using System.Reflection.Emit;

namespace NForth3
{
    class Memory
    {
        public const int MEMORY_SIZE = 1024 * 1024;
        public FieldInfo MemoryArrayField { set;  get; }
        public FieldInfo HereField { set; get; }
        public Type ClassType { set; get; }

        public Memory(NForth nforth)
        {
            CreateType(nforth);

            // initialize memory
            ClassType.GetMethod("Init").Invoke(null, null);
        }

        private void CreateType(NForth nforth)
        {
            ModuleBuilder module = nforth.Assembly.DefineDynamicModule("Memory");
            TypeBuilder type = module.DefineType("Memory", TypeAttributes.Public);

            // fields
            FieldBuilder memory_field = type.DefineField("MemoryArray", typeof(object[]), FieldAttributes.Static | FieldAttributes.Public);
            FieldBuilder here_field = type.DefineField("Here", typeof(uint), FieldAttributes.Static | FieldAttributes.Public);

            // init method
            MethodBuilder init = type.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Static);
            ILGenerator il = init.GetILGenerator();
            il.Emit(OpCodes.Ldc_I4, MEMORY_SIZE);
            il.Emit(OpCodes.Newarr, typeof(object));
            il.Emit(OpCodes.Stsfld, memory_field);
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stsfld, here_field);
            il.Emit(OpCodes.Ret);

            type.CreateType();
            module.CreateGlobalFunctions();

            ClassType = module.GetType("Memory");
            MemoryArrayField = ClassType.GetField("MemoryArray");
            HereField = ClassType.GetField("Here");
        }
    }
}
